#!/usr/bin/env python
"""
##############################################################################
测试：./visitor.py dir testMask [searchkey]
子类服装os.walk调用方法某些细节，以便进行遍历和搜索；testmask是一个整数比特掩码，每个可用的
自我测试占一位；另请参考visitor_*.py子类用例；框架中一般应当使用__X作为伪局部名称，不过为了
子类和客户端的使用，这里的所有名称都将导出；可重新定义reset以支持多个需要更新的子类的相互独立
的遍历操作。
##############################################################################
"""

import os
import sys

DOLIST = 1
DOSEARCH = 2  # 3 = 进行列出和搜索
DONEXT = 4  # 添加了下一个测试时


class FileVisitor:
    """
    访问startDir(默认为“.”)下所有非目录文件；可通过重载visit*方法定制文件/目录处理器；情景
    参数/属性为可选的子类特异的状态；追踪开关：0代表关闭，1代表显示目录，2代表显示目录及文件。
    """

    def __init__(self, context=None, trace=0):
        self.fileCount = 0
        self.dirCount = 0
        self.context = context
        self.trace = trace

    def run(self, startDir=os.curdir, reset=True):
        if reset:
            self.reset()
        for thisDir, subsHere, filesHere in os.walk(startDir):
            self.visitDir(thisDir)
            for file in filesHere:
                filePath = os.path.join(thisDir, file)
                self.visitFile(filePath)

    def reset(self):
        "为了重复使用遍历器和相互独立的便利操作"
        self.fileCount = self.dirCount = 0

    def visitDir(self, dir):
        "待重写或拓展"
        self.dirCount += 1
        if self.trace > 0:
            print(dir, '...')

    def visitFile(self, file):
        "待重写或拓展"
        self.fileCount += 1
        if self.trace > 1:
            print(self.fileCount, '->', file)


class SearchVisitor(FileVisitor):
    """
    在startDir及其子目录中的文件中搜索字符串；
    子类：根据需要重新定义visitMatch、拓展列表和候选；子类可以使用testExts来指定进行搜索的文
    件类型（还可以重定义candidate以对文本内容使用MIME Tyeps（文本类型识别））
    """
    skipExts = []
    testExts = [
        '.txt', '.c', '.cpp', '.py', '.pyw', 'html', '.h'
    ]  # 搜索带有这些拓展名的文件
    # skipExts = [
    #     '.gif', '.png', '.jpg', '.jpeg', '.pyc', '.o', '.a', '.exe', '.mp4'
    # ]  # 或者跳过带有这些拓展名的文件

    def __init__(self, searchkey, trace=2):
        FileVisitor.__init__(self, searchkey, trace)
        self.searchCount = 0

    def reset(self):
        self.searchCount = 0

    def candidate(self, fn):
        ext = os.path.splitext(fn)[1]
        if self.testExts:
            return ext in self.testExts
        else:
            return ext not in self.skipExts

    def visitFile(self, fp):
        FileVisitor.visitFile(self, fp)

        if self.candidate(fp):
            try:
                text = open(fp).read()
            except Exception:
                print('错误：', fp, sys.exc_info()[0], sys.exc_info()[1])
            else:
                if self.context in text:
                    self.searchCount += 1
                    self.visitMatch(fp, text)
        else:
            if self.trace > 0:
                print('跳过', fp)

    def visitMatch(self, fn, text):
        print(fn, '匹配到了', self.context)


def _selftest(testMask):
    if testMask & DOLIST:
        visitor = FileVisitor(trace=2)
        visitor.run(sys.argv[1])
        print('访问了', visitor.dirCount, '个目录，', visitor.fileCount, '个文件。')
    if testMask & DOSEARCH:
        searcher = SearchVisitor(sys.argv[3], trace=0)
        searcher.run(sys.argv[1])
        print('访问了', searcher.fileCount, '个文件，匹配了', searcher.searchCount, '个。')


if __name__ == '__main__':
    _selftest(int(sys.argv[2]))
